// SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
// SPDX-License-Identifier: GPL-3.0-only

package badger_dao_impl

import (
	"context"
	"fmt"
	"path/filepath"
	"sort"
	"strings"

	"gitcode.com/openseaotter/so_proto_gen_go.git/image_api"
	"gitcode.com/openseaotter/so_server/storage/core"
	"gitcode.com/openseaotter/so_server/utils"
	"github.com/dgraph-io/badger/v4"
	"google.golang.org/protobuf/proto"
)

type ImageInfoList []*image_api.ImageInfo

func (a ImageInfoList) Len() int           { return len(a) }
func (a ImageInfoList) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ImageInfoList) Less(i, j int) bool { return a[i].CreateTime < a[j].CreateTime }

type _ImageInfoDao struct {
	dbh *badger.DB
}

func NewImageInfoDao() (core.ImageInfoDao, error) {
	dbPath := filepath.Join(utils.DATA_DIR_PATH, "image_info")
	options := badger.DefaultOptions(dbPath)
	dbh, err := badger.Open(options)
	if err != nil {
		return nil, err
	}
	return &_ImageInfoDao{dbh: dbh}, nil
}

func (db *_ImageInfoDao) Insert(ctx context.Context, image *image_api.ImageInfo) error {
	return db.Update(ctx, image)
}

func (db *_ImageInfoDao) genKey(groupName, imageName string) string {
	return strings.Join([]string{groupName, imageName}, "\t")
}

func (db *_ImageInfoDao) Exist(ctx context.Context, groupName, imageName string) (bool, error) {
	txn := db.dbh.NewTransaction(false)
	defer txn.Discard()

	key := db.genKey(groupName, imageName)
	item, err := txn.Get([]byte(key))
	if err != nil {
		if err == badger.ErrKeyNotFound {
			return false, nil
		}
		return false, err
	}
	if item.IsDeletedOrExpired() {
		return false, nil
	}
	return true, nil
}

func (db *_ImageInfoDao) Get(ctx context.Context, groupName, imageName string) (*image_api.ImageInfo, error) {
	txn := db.dbh.NewTransaction(false)
	defer txn.Discard()

	key := db.genKey(groupName, imageName)
	item, err := txn.Get([]byte(key))
	if err != nil {
		return nil, err
	}
	if item.IsDeletedOrExpired() {
		return nil, badger.ErrKeyNotFound
	}
	value, err := item.ValueCopy(nil)
	if err != nil {
		return nil, err
	}

	imageInfo := &image_api.ImageInfo{}
	err = proto.Unmarshal(value, imageInfo)
	if err != nil {
		return nil, err
	}
	return imageInfo, nil
}

func (db *_ImageInfoDao) Update(ctx context.Context, image *image_api.ImageInfo) error {
	value, err := proto.Marshal(image)
	if err != nil {
		return err
	}

	txn := db.dbh.NewTransaction(true)
	defer txn.Discard()

	key := db.genKey(image.GroupName, image.ImageName)
	err = txn.Set([]byte(key), value)
	if err != nil {
		return err
	}

	return txn.Commit()
}

func (db *_ImageInfoDao) Remove(ctx context.Context, groupName, imageName string) error {
	txn := db.dbh.NewTransaction(true)
	defer txn.Discard()

	key := db.genKey(groupName, imageName)
	err := txn.Delete([]byte(key))
	if err != nil {
		return err
	}
	return txn.Commit()
}

func (db *_ImageInfoDao) Count(ctx context.Context, groupName string) (uint32, error) {
	txn := db.dbh.NewTransaction(false)
	defer txn.Discard()

	keyPrefix := []byte(fmt.Sprintf("%s\t", groupName))
	options := badger.IteratorOptions{
		PrefetchValues: false,
		PrefetchSize:   100,
		Reverse:        false,
		AllVersions:    false,
		Prefix:         keyPrefix,
	}

	retCount := uint32(0)

	iter := txn.NewIterator(options)
	defer iter.Close()

	for iter.Rewind(); iter.Valid(); iter.Next() {
		item := iter.Item()
		if item.IsDeletedOrExpired() {
			continue
		}
		retCount += 1
	}
	return retCount, nil
}

func (db *_ImageInfoDao) List(ctx context.Context, filterByGroupName bool, groupName string) ([]*image_api.ImageInfo, error) {
	txn := db.dbh.NewTransaction(false)
	defer txn.Discard()

	var keyPrefix []byte
	if filterByGroupName {
		keyPrefix = []byte(fmt.Sprintf("%s\t", groupName))
	}

	options := badger.IteratorOptions{
		PrefetchValues: true,
		PrefetchSize:   100,
		Reverse:        false,
		AllVersions:    false,
		Prefix:         keyPrefix,
	}

	iter := txn.NewIterator(options)
	defer iter.Close()

	imageInfoList := []*image_api.ImageInfo{}

	for iter.Rewind(); iter.Valid(); iter.Next() {
		item := iter.Item()
		if item.IsDeletedOrExpired() {
			continue
		}
		value, err := item.ValueCopy(nil)
		if err != nil {
			return nil, err
		}
		imageInfo := &image_api.ImageInfo{}
		err = proto.Unmarshal(value, imageInfo)
		if err != nil {
			return nil, err
		}
		imageInfoList = append(imageInfoList, imageInfo)
	}
	sort.Sort(sort.Reverse(ImageInfoList(imageInfoList)))
	return imageInfoList, nil
}
